1.1 LLDB
LLDB全称为low level debugger,是xcode自带的动态调试工具,可用于c,c++,objective-c,全盘支持iOS,OSX以及iOS模拟器。
LLDB有以下四个功能:
1.在特定的情况下暂停程序
2.在特定的情况下启动程序
3.在程序停止的情况下检查程序内部
4.在程序停止的情况下改动程序,观察执行过程。
LLDB是运行在OSX程序中的,可以通过特定的语句配合debugServer对iOS中的程序进行监视。
1.2 debugServer
debugserver是运行在iOS程序中的,顾名思义,作为server端,它接受与来自服务端,也就是LLDB传送过来的命令,继而执行的操作。再把执行结果返回给LLDB,默认情况下, iOS并没有安装debugserver,只有在设备连接了一次xcode之后,并在window->device中添加此设备之后,debugservercai才会别安装到iOS中,默认位于Developer/usr/bin目录下。
但是默认的debugserver只用用户调试我们自己的app,如果想要调试iOS里面所有的app,还需要通过给debugserver赋予task_for_pid的权限。以下是配置task_for_pid的教程
1.debugserver瘦身,首先根据你的iOS设备确定你的手机对应的ARM,4s是armv7,5,5c是armv7s,5s以上的机型都是arm64,我的手机是5s,所以对应的arm是arm64,所以只从debugserver保留arm64的架构即可。具体步骤:
首先将未经处理的debugserver从iOS拷贝到OSX中,可以用scp命令,也可以用iFunBox。
然后利用命令
1 | lipo -thin arm64(这里对应你的iOS设备的结构) (未经处理的debugServer的路径) -output (新的debugserver的保存路径) |
2.给debugServer添加task_for_pid权限,将以下xml保存为ent.xml。
1 | <plist version="1.0"> |
运行以下命令:
1 | ldid -S(ent.xml的全路径) (debugServer的全路径) |
以上命令实质上是通过ldid这个签名工具,为debugserver添加task_for_pid权限,上面的命令也可以用xcode自带的codesign实现。
1 | codesign -s -f - --entitlements ent.plist -f debugserver |
3.将添加权限后的debugserver放回去iOS复制到/usr/bin/debugserver,这样的好处是可以全局执行debugserver命令。另一个原因是因为在developer里面的是不可以写的,无法覆盖。
4.给新的debugserver添加可执行的权限
1 | chmod +x /usr/bin/debugserver |
####1.3 debugserver使用
debugserver有两个命令。
1.启动进程,debugserver会启动进程,并且开启port端口,等待来自ip的LLDB接入。ip如果为*则代表任意ip
1 | debugserver backend ip:port /path/to/excutable |
2.附加进程,附加进程是在进程已打开的情况下,可以执行以下命令。
1 | debugserver ip:port -a "processname" |
1.4 LLDB && debugserver配合使用
假设我们要调试在iOS中要按下home键的处理。通过IDA我们知道按下home键的时候调用了 -[SpringBoard _menuButtonDown:] 这个方法,现在我们的需求是在menuButtonDown的方法下面打一个断点。
(1)首先利用debugserver启动进程或者附加进程。系统的SpringBoard默认是启动的,所以不需要启动进程,使用附加进程命令即可。SSH到iOS中,然后附加到SpringBoard进程(以下命令开启了1234接口并且等待来自任意ip的LLDB接入)
1 | Last login: Sun Apr 8 11:04:23 on ttys016 |
(2)在OSX的terminal中启动LLDB,然后连接远程1234端口
1 | MacBookPro:~ lemon$ lldb |
(3)通过以上两步我们已经可以在电脑的终端利用LLDB调试APP了。现在在终端输入 image list -o -f 可以看到当前所有iOS中启动的所有的进程模块。
1 | [(lldb) image list -o -f |
(4)通过3中的信息我们发现了Spring的偏移是0x00000000000dc000,接下来我们需要用ida查看menuButtonDown这个函数在模块中的地址是多少。
1 | -[SpringBoard _menuButtonDown:]: |
通过上面的信息可以看到,该函数的第一条指令的地址是0000000100016fe8,所以该函数偏移后的地址是:原地址+偏移 =》0x00000000000dc000 + 0000000100016fe8 = 0x1000F2FE8
(5)在偏移后的地址出打断点
1 | (lldb) br s -a 0x1000F2FE8 |
(6)在打完断点之后,可以按下home键,这个时候程序就会停在断点处,可以用1.5里面的命令进行调试等等
1.5 LLDB命令解析
(1) b NSog // 在函数的起始位置设置断点
(2) br s -a address //在地址处设置断点
(3) br s -a ‘address + offsetAddress’ //在地址处设置断点
(4) br dis //禁用所有的断点
(5) br dis 6 //禁用序号为6的断点
(6) br del //删除所有断点
(7) br del 6 //删除序号为6的断点
(8) br en //启用所有断点
(9) br en 6 //启用序号为6的断点
(10)br com add 1 //当序号为1的断点执行的时候,可以执行预先设置的指令。当执行了这条指令之后,llDB会要求你输入一些指令,并且以DONE结束,当程序停在序号为1的断点时,就会执行这些预先设定的指令。
(11)p $R1 //打印R1的值
(12)p/x $sp //打印SP的地址,以16进制输出
(13)x/10 $sp //打印sp的地址,以10进制输出
(14)nexti 和 stepi //nexti不进入函数体,stepi进入函数体,nexti可以简写成ni,setpi可以简写成si。
(15)register write r0 1 //用于给指定的寄存器r0赋值为1从而达到对程序进行改动的目的。